package websocket

import (
	"errors"
	"github.com/gorilla/websocket"
	"net/http"
	"sync"
	"time"
)

// http升级websocket协议的配置
var wsUpgrader = websocket.Upgrader{
	// 允许所有CORS跨域请求
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

// 客户端读写消息
type WsMessage struct {
	MessageType int
	Data        []byte
}

type Client struct {
	Key           string          // 客户端Key
	Addr          string          // 客户端地址
	Socket        *websocket.Conn // 客户端连接
	AppId         uint32          // AppId
	FirstTime     uint64          // 首次连接事件
	HeartbeatTime uint64          // 用户上次心跳时间
	inChan        chan *WsMessage // 读队列
	outChan       chan *WsMessage // 写队列
	closeChan     chan byte       // 关闭通知
	mutex         sync.Mutex      // 避免重复关闭管道
	isClosed      bool
}

func NewClient(resp http.ResponseWriter, req *http.Request) (*Client, error) {
	// 应答客户端告知升级连接为websocket
	wsSocket, err := wsUpgrader.Upgrade(resp, req, nil)
	if err != nil {
		return nil, err
	}

	firstTime := uint64(time.Now().Unix())
	addr := wsSocket.RemoteAddr().String()
	client := &Client{
		Addr:          addr,
		FirstTime:     firstTime,
		HeartbeatTime: firstTime,
		Socket:        wsSocket,
		inChan:        make(chan *WsMessage, 1000),
		outChan:       make(chan *WsMessage, 1000),
		closeChan:     make(chan byte),
		isClosed:      false,
	}

	// 读协程
	go client.wsReadLoop()
	// 写协程
	go client.wsWriteLoop()

	return client, nil
}

func (c *Client) wsReadLoop() {
	for {
		// 读一个message
		msgType, data, err := c.Socket.ReadMessage()
		if err != nil {
			goto error
		}
		req := &WsMessage{
			msgType,
			data,
		}
		// 放入请求队列
		select {
		case c.inChan <- req:
		case <-c.closeChan:
			goto closed
		}
	}
error:
	c.WsClose()
closed:
}

func (c *Client) wsWriteLoop() {
	for {
		select {
		// 取一个应答
		case msg := <-c.outChan:
			// 写给websocket
			if err := c.Socket.WriteMessage(msg.MessageType, msg.Data); err != nil {
				goto error
			}
		case <-c.closeChan:
			goto closed
		}
	}
error:
	c.WsClose()
closed:
}

func (c *Client) WsWrite(messageType int, data []byte) error {
	select {
	case c.outChan <- &WsMessage{messageType, data,}:
	case <-c.closeChan:
		return errors.New("websocket closed")
	}
	return nil
}

func (c *Client) WsRead() (*WsMessage, error) {
	select {
	case msg := <-c.inChan:
		return msg, nil
	case <-c.closeChan:
	}
	return nil, errors.New("websocket closed")
}

func (c *Client) WsClose() {
	_ = c.Socket.Close()

	c.mutex.Lock()
	defer c.mutex.Unlock()
	if !c.isClosed {
		c.isClosed = true
		close(c.closeChan)
	}

	WsServer.DelClients(c.Key)
}

func (c *Client) Heartbeat(currentTime uint64) {
	c.HeartbeatTime = currentTime
	return
}